package com.dylan.distruptor;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author dylan
 * @Description: 队列数据结构
 * @Create: 2024/11/11 16:11
 */
public class DylanArrayQueue {


    private Object[] queue;


    private int readIndex;

    private int writeIndex;

    private final int capacity;

    private final ReentrantLock lock;

    private final Condition fullConditional;
    private final Condition emptyConditional;

    private int count;

    public DylanArrayQueue(int capacity) {
        this.capacity = initCapacity(capacity);
        queue = new Object[capacity];
        lock = new ReentrantLock();
        fullConditional = lock.newCondition();
        emptyConditional = lock.newCondition();
    }

    private Integer initCapacity(Integer capacity) {
        int cp = capacity - 1;
        cp |= cp >>> 1;
        cp |= cp >>> 2;
        cp |= cp >>> 4;
        cp |= cp >>> 8;
        cp |= cp >>> 16;
        return cp + 1;
    }


    public void put(Object value) {
        lock.lock();
        try {
            while (count == queue.length) {
                //阻塞
                fullConditional.await();
            }
            queue[writeIndex] = value;

            if (++writeIndex == capacity) {
                //重置写指针
                writeIndex = 0;
            }
            count++;
            emptyConditional.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }


    public Object take() {
        lock.lock();
        Object[] items = queue;
        Object item = null;
        try {
            while (count == 0) {
                emptyConditional.await();
            }
            item = items[readIndex];
            queue[readIndex] = null; //helpGc

            if (++readIndex == queue.length) {
                readIndex = 0;
            }

            count--;
            fullConditional.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return item;
    }

}
